package com.gitee.sparta.core.process.operate.redis;

import cn.hutool.core.collection.CollUtil;
import com.gitee.sparta.common.SpartaConstant;
import com.gitee.sparta.common.SpartaLuaConstant;
import com.gitee.sparta.common.helpers.JacksonHelper;
import com.gitee.sparta.common.helpers.ScriptShaHelper;
import com.gitee.sparta.core.bean.metadata.Metadata;
import com.gitee.sparta.core.process.operate.DenyTabOperate;
import com.gitee.sparta.core.property.SpartaCoreProperties;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;

import java.util.*;

/**
 * <p>
 *      使用 lua 脚本针对 deny_tab 中进行操作
 * </p>
 *
 * @author zyred
 * @since v 0.0.1
 **/
@Slf4j
public class LuaDenyTabOperate<V> implements DenyTabOperate<V> {

    private final RedissonClient redissonClient;
    private final SpartaCoreProperties properties;

    public LuaDenyTabOperate(SpartaCoreProperties properties, RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
        this.properties = properties;
    }

    @Override
    public void doProcessDenyPersistence(String fullTopic, final String readableTabKey, final String denyTabKey) {
        Map<String, String> cache = SpartaLuaConstant.getSha(SpartaConstant.DENY_PERSISTENCE);
        final List<Object> keys = Arrays.asList(denyTabKey, readableTabKey);

        try {
            RScript script = this.redissonClient.getScript(CodecCache.getCodec(StringCodec.class));
            script.evalSha(RScript.Mode.READ_ONLY, cache.get(SpartaConstant.LUA_SHA_KEY),
                    RScript.ReturnType.VALUE, keys, fullTopic, fullTopic);
        } catch (Exception ex) {
            log.error("[Sparta] Process deny persistence failed to execute, topic: {}, exception：{}",
                    fullTopic, ex.getMessage(), ex);
            ScriptShaHelper.reloadScriptSha(ex, this.redissonClient, this.properties);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<V> processDenys(String denyTabKey, String metadataTabKey) {
        final List<Object> keys = Arrays.asList(denyTabKey, metadataTabKey);

        Map<String, String> cache = SpartaLuaConstant.getSha(SpartaConstant.PROCESS_DENYS);
        RScript script = this.redissonClient.getScript(CodecCache.getCodec(StringCodec.class));

        try {
            Object result = script.evalSha(RScript.Mode.READ_ONLY, cache.get(SpartaConstant.LUA_SHA_KEY),
                    RScript.ReturnType.VALUE, keys);

            if (Objects.nonNull(result) && result instanceof List) {
                List<Object> metadataList = (List<Object>) result;
                if (CollUtil.isEmpty(metadataList)) {
                    return Collections.emptyList();
                }

                List<V> vs = new ArrayList<>(metadataList.size());
                for (Object metadata : metadataList) {
                    Metadata<V> deserialize = JacksonHelper.deserialize(String.valueOf(metadata), Metadata.class);
                    vs.add(deserialize.getBody());
                }
                return vs;
            }
            return Collections.emptyList();
        } catch (Exception ex) {
            log.error("[Sparta] Process denys exception : {}", ex.getMessage(), ex);
            ScriptShaHelper.reloadScriptSha(ex, this.redissonClient, this.properties);
        }
        return Collections.emptyList();
    }
}
